package pf;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import basic.*;

public class PF_test1 {

	StatisticsMgr pStatisticsMgr;
	private static String FILE1;
	private static String FILE2;
	/**
	 * @param args
	 * @throws IOException 
	 */
	public static int main(String[] args) throws IOException {

		ReturnCode rc;

		// Write out initial starting message
		System.err.flush();
		System.out.flush();
		System.out.println("Starting PF layer test.\n");
		System.out.flush();

		// If we are tracking the PF Layer statistics
		System.out.println("Note: Statistics are turned on.\n");

		// Delete files from last time
		File F1 = new File(FILE1);
		File F2 = new File(FILE2);

		F1.delete();
		F2.delete();

		// Do tests
		PF_test1 t1 = new PF_test1();
		
		if ((rc = t1.TestPF()) != ReturnCode.PF_SUCCESS)
			System.out.println("Error occur !");
		
		
		/*
		if ((rc = t1.TestHash()) != ReturnCode.PF_SUCCESS) 
			System.out.println("Error occur !");
		*/
		

		// Write ending message and exit
		System.out.println("Ending PF layer test.\n\n");

		return (0);

	}



	//
	//File:        pf_test1.cc
	//Description: Test PF component
	//Authors:     Dallan Quass (quass@cs.stanford.edu)
	//           Jason McHugh (mchughj@cs.stanford.edu)
	//
	//1997: Added call to confirm the statistics from the buffer mgr
	//

	//
	//PF_ConfirmStatistics
	//
	//This function will be run at the end of the program after all the tests
	//to confirm that the buffer manager operated correctly.
	//
	//These numbers have been confirmed.  Note that if you change any of the
	//tests, you will also need to change these numbers as well.
	//
	void PF_ConfirmStatistics()
	{
		// Must remember to delete the memory returned from StatisticsMgr::Get
		System.out.println("Verifying the statistics for buffer manager: ");
		int piGP = pStatisticsMgr.Get("GetPage");
		int piPF = pStatisticsMgr.Get("PageFound");
		int piPNF = pStatisticsMgr.Get("PageNotFound");
		int piWP = pStatisticsMgr.Get("WritePage");
		int piRP = pStatisticsMgr.Get("ReadPage");
		int piFP = pStatisticsMgr.Get("FlushPage");

		if (piGP !=0 && (piGP != 702)) {
			System.out.println("Number of GetPages is incorrect! (" + piGP + ")\n");
			// No built in error code for this
			System.exit(0);
		}
		if (piPF!=0 && (piPF != 23)) {
			System.out.println("Number of pages found in the buffer is incorrect! ("+piPF +")\n");
			// No built in error code for this
			System.exit(0);
		}
		if (piPNF!=0 && (piPNF != 679)) {
			System.out.println("Number of pages not found in the buffer is incorrect! ("+piPNF+")\n");
			// No built in error code for this
			System.exit(0);
		}
		if (piRP!=0 && (piRP != 679)) {
			System.out.println("Number of read requests to the Unix file system is "+"incorrect! ("+piPNF+")\n");
			// No built in error code for this
			System.exit(0);
		}
		if (piWP!=0 && (piWP != 339)) {
			System.out.println("Number of write requests to the Unix file system is "+"incorrect! ("+piPNF+")\n");
			// No built in error code for this
			System.exit(0);
		}
		if (piFP!=0 && (piFP != 16)) {
			System.out.println("Number of requests to flush the buffer is "+"incorrect! ("+piPNF+")\n");
			// No built in error code for this
			System.exit(0);
		}
		System.out.println(" Correct!\n");

	}

	//
	//Defines
	//

	//
	//Function declarations
	//

	ReturnCode WriteFile(PF_Manager pfm, String fname) throws IOException
	{
		PF_FileHandle fh = null;
		PF_PageHandle ph = null;
		ReturnCode            rc;
		BytePointer pData = null;
		IntegerWrapper      pageNum = null;
		int           i;

		System.out.println("Opening file: "+fname+"\n");

		if ((rc = pfm.OpenFile(fname, fh)) != ReturnCode.PF_SUCCESS)
			return(rc);

		for (i = 0; i < Constant.PF_BUFFER_SIZE; i++) {
			if ((rc = fh.allocatePage(ph))!= ReturnCode.PF_SUCCESS ||
					(rc = ph.GetData(pData))!= ReturnCode.PF_SUCCESS ||
					(rc = ph.GetPageNum(pageNum)) != ReturnCode.PF_SUCCESS)
				return(rc);

			if (i != pageNum.GetValue()) {
				System.out.println("Page number incorrect: "+pageNum.GetValue()+" "+i+"\n");
				System.exit(0);
			}

			//memcpy(pData, (char *)&pageNum, sizeof(PageNum));
			//  memcpy(pData + PF_PAGE_SIZE - sizeof(PageNum), &pageNum, sizeof(PageNum));

			System.out.println("Page allocated: "+pageNum.GetValue()+"\n");
		}

		// Test pinning too many pages
		if ((rc = fh.allocatePage(ph)) != ReturnCode.PF_NOBUF) {
			System.out.println("Pin too many pages should fail: ");
			return(rc);
		}

		System.out.println("Unpinning pages and closing the file\n");

		for (i = 0; i < Constant.PF_BUFFER_SIZE; i++){


			if ((rc = fh.unpinPage(new PageNum(i))) != ReturnCode.PF_SUCCESS)
				return(rc);
		}

		if ((rc = pfm.CloseFile(fh)) != ReturnCode.PF_SUCCESS)
			return(rc);

		// Return ok
		return ReturnCode.PF_SUCCESS;

	}

	ReturnCode PrintFile(PF_FileHandle fh)
	{
		PF_PageHandle ph = null;
		ReturnCode            rc;
		BytePointer pData = null;
		IntegerWrapper pageNum = null, temp = null;

		System.out.println("Reading file\n");

		if ((rc = fh.getFirstPage(ph)) != ReturnCode.PF_SUCCESS)
			return (rc);

		do {
			if ((rc = ph.GetData(pData)) != ReturnCode.PF_SUCCESS ||
					(rc = ph.GetPageNum(pageNum))!= ReturnCode.PF_SUCCESS )
				return(rc);

			//memcpy((char *)&temp, pData, sizeof(PageNum));
			
			pData.addr = Arrays.copyOf(temp.toString().getBytes(), temp.toString().length());

			System.out.println("Got page: "+pageNum+" "+temp+"\n");

			if ((rc = fh.unpinPage(new PageNum(pageNum.GetValue()))) != ReturnCode.PF_SUCCESS)
				return(rc);
		} while ((rc = fh.getNextPage(new PageNum(pageNum.GetValue()), ph)) != ReturnCode.PF_SUCCESS);

		if (rc != ReturnCode.PF_EOF)
			return(rc);

		System.out.println("EOF reached\n");

		// Return ok
		return ReturnCode.PF_SUCCESS;
	}

	ReturnCode ReadFile(PF_Manager pfm, String fname) throws IOException
	{
		PF_FileHandle fh = null;
		ReturnCode            rc;

		System.out.println("Opening: "+fname+"\n");

		if ((rc = pfm.OpenFile(fname, fh)) != ReturnCode.PF_SUCCESS||
				(rc = PrintFile(fh)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.CloseFile(fh)) != ReturnCode.PF_SUCCESS)
			return (rc);
		else
			return ReturnCode.PF_SUCCESS;
	}

	 ReturnCode TestPF() throws IOException{

		PF_Manager    pfm = null;
		PF_FileHandle fh1 = null, fh2 = null;
		PF_PageHandle ph = null;
		ReturnCode            rc;
		BytePointer pData = null;
		IntegerWrapper       pageNum = null, temp = null;
		int           i;

		int len = 0;
		pfm.GetBlockSize(len);
		System.out.println("get bock size returned " + len);
		System.out.println("Creating and opening two files\n");

		if ((rc = pfm.CreateFile(FILE1)) != ReturnCode.PF_SUCCESS ||
				(rc = pfm.CreateFile(FILE2)) != ReturnCode.PF_SUCCESS ||
				(rc = WriteFile(pfm, FILE1)) != ReturnCode.PF_SUCCESS ||
				(rc = ReadFile(pfm, FILE1)) != ReturnCode.PF_SUCCESS ||
				(rc = WriteFile(pfm, FILE2)) != ReturnCode.PF_SUCCESS ||
				(rc = ReadFile(pfm, FILE2)) != ReturnCode.PF_SUCCESS ||
				(rc = pfm.OpenFile(FILE1, fh1)) != ReturnCode.PF_SUCCESS ||
				(rc = pfm.OpenFile(FILE2, fh2))!= ReturnCode.PF_SUCCESS )
			return(rc);

		System.out.println("Disposing of alternate pages\n");

		for (i = 0; i < Constant.PF_BUFFER_SIZE; i++) {

			if ((rc = fh1.disposePage(new PageNum(i))) != ReturnCode.PF_SUCCESS)
				return(rc);
			else
				if ((rc = fh2.disposePage(new PageNum(i))) != ReturnCode.PF_SUCCESS)
					return(rc);
		}

		System.out.println("Closing and destroying both files\n");

		if ((rc = fh1.flushPages()) != ReturnCode.PF_SUCCESS||
				(rc = fh2.flushPages()) != ReturnCode.PF_SUCCESS||
				(rc = pfm.CloseFile(fh1)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.CloseFile(fh2)) != ReturnCode.PF_SUCCESS||
				(rc = ReadFile(pfm, FILE1)) != ReturnCode.PF_SUCCESS||
				(rc = ReadFile(pfm, FILE2)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.DestroyFile(FILE1)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.DestroyFile(FILE2))!= ReturnCode.PF_SUCCESS)
			return(rc);

		System.out.println("Creating and opening files again\n");

		if ((rc = pfm.CreateFile(FILE1)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.CreateFile(FILE2)) != ReturnCode.PF_SUCCESS||
				(rc = WriteFile(pfm, FILE1)) != ReturnCode.PF_SUCCESS||
				(rc = WriteFile(pfm, FILE2)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.OpenFile(FILE1, fh1)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.OpenFile(FILE2, fh2))!= ReturnCode.PF_SUCCESS)
			return(rc);

		System.out.println("Allocating additional pages in both files\n");

		for (i = Constant.PF_BUFFER_SIZE; i < Constant.PF_BUFFER_SIZE * 2; i++) {
			if ((rc = fh2.allocatePage(ph)) != ReturnCode.PF_SUCCESS||
					(rc = ph.GetData(pData)) != ReturnCode.PF_SUCCESS||
					(rc = ph.GetPageNum(pageNum))!= ReturnCode.PF_SUCCESS)
				return(rc);

			if (i != pageNum.GetValue()) {
				System.out.println("Page number is incorrect:"+pageNum+" "+i+"\n");
				System.exit(0);
			}

			//memcpy(pData, (char*)&pageNum, sizeof(PageNum));
			pData.addr = Arrays.copyOf(pageNum.toString().getBytes(), pageNum.toString().length());

			if ((rc = fh2.markDirty(new PageNum(pageNum.GetValue()))) != ReturnCode.PF_SUCCESS||
					(rc = fh2.unpinPage(new PageNum(pageNum.GetValue())))!= ReturnCode.PF_SUCCESS)
				return(rc);

			if ((rc = fh1.allocatePage(ph)) != ReturnCode.PF_SUCCESS ||
					(rc = ph.GetData(pData)) != ReturnCode.PF_SUCCESS ||
					(rc = ph.GetPageNum(pageNum))!= ReturnCode.PF_SUCCESS )
				return(rc);

			if (i != pageNum.GetValue()) {
				System.out.println("Page number is incorrect:"+pageNum+" "+i+"\n");
				System.exit(0);
			}

			//memcpy(pData, (char*)&pageNum, sizeof(PageNum));
			pData.addr = Arrays.copyOf(pageNum.toString().getBytes(), pageNum.toString().length());
			// memcpy(pData + PF_PAGE_SIZE - sizeof(PageNum), &pageNum, sizeof(PageNum));

			if ((rc = fh1.markDirty(new PageNum(pageNum.GetValue()))) != ReturnCode.PF_SUCCESS||
					(rc = fh1.unpinPage(new PageNum(pageNum.GetValue())))!= ReturnCode.PF_SUCCESS)
				return(rc);
		}

		System.out.println("Disposing of alternate additional pages\n");

		for (i = Constant.PF_BUFFER_SIZE; i < Constant.PF_BUFFER_SIZE * 2; i++) {

				if ((rc = fh1.disposePage(new PageNum(i)))!= ReturnCode.PF_SUCCESS)
					return(rc);
			else
				if ((rc = fh2.disposePage(new PageNum(i)))!= ReturnCode.PF_SUCCESS)
					return(rc);
		}

		System.out.println("Getting file 2 remaining additional pages\n");

		for (i = Constant.PF_BUFFER_SIZE; i < Constant.PF_BUFFER_SIZE * 2; i++) {
			
				if ((rc = fh2.getThisPage(new PageNum(i), ph)) != ReturnCode.PF_SUCCESS||
						(rc = ph.GetData(pData)) != ReturnCode.PF_SUCCESS||
						(rc = ph.GetPageNum(pageNum))!= ReturnCode.PF_SUCCESS)
					return(rc);

				//memcpy((char *)&temp, pData, sizeof(PageNum));
				pData.addr = Arrays.copyOf(temp.toString().getBytes(), temp.toString().length());

				System.out.println("Page: "+pageNum+" "+temp+"\n");

				if ((rc = fh2.unpinPage(new PageNum(i)))!= ReturnCode.PF_SUCCESS)
					return(rc);
			
		}

		System.out.println("Getting file 1 remaining additional pages\n");

		for (i = Constant.PF_BUFFER_SIZE; i < Constant.PF_BUFFER_SIZE * 2; i++) {
			
				if ((rc = fh1.getThisPage(new PageNum(i), ph)) != ReturnCode.PF_SUCCESS||
						(rc = ph.GetData(pData)) != ReturnCode.PF_SUCCESS||
						(rc = ph.GetPageNum(pageNum))!= ReturnCode.PF_SUCCESS)
					return(rc);

				//memcpy((char *)&temp, pData, sizeof(PageNum));
				pData.addr = Arrays.copyOf(pageNum.toString().getBytes(), pageNum.toString().length());

				System.out.println("Page: "+pageNum+" "+temp+"\n");

				if ((rc = fh1.unpinPage(new PageNum(pageNum.GetValue())))!= ReturnCode.PF_SUCCESS)
					return(rc);
				
			
		}

		System.out.println("Printing file 2, then file 1\n");

		if ((rc = PrintFile(fh2)) != ReturnCode.PF_SUCCESS||
				(rc = PrintFile(fh1))!= ReturnCode.PF_SUCCESS)
			return(rc);

		System.out.println("Putting stuff into the holes of file 1\n");

		for (i = 0; i < Constant.PF_BUFFER_SIZE / 2; i++) {
			if ((rc = fh1.allocatePage(ph)) != ReturnCode.PF_SUCCESS||
					(rc = ph.GetData(pData)) != ReturnCode.PF_SUCCESS||
					(rc = ph.GetPageNum(pageNum))!= ReturnCode.PF_SUCCESS)
				return(rc);

			//memcpy(pData, (char*)&pageNum, sizeof(PageNum));
			pData.addr = Arrays.copyOf(pageNum.toString().getBytes(), pageNum.toString().length());
			// memcpy(pData + PF_PAGE_SIZE - sizeof(PageNum), &pageNum, sizeof(PageNum));

			if ((rc = fh1.markDirty(new PageNum(pageNum.GetValue()))) != ReturnCode.PF_SUCCESS||
					(rc = fh1.unpinPage(new PageNum(pageNum.GetValue())))!= ReturnCode.PF_SUCCESS)
				return(rc);
			
		}

		System.out.println("Print file 1 and then close both files\n");

		if ((rc = PrintFile(fh1)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.CloseFile(fh1)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.CloseFile(fh2))!= ReturnCode.PF_SUCCESS)
			return(rc);

		System.out.println("Reopen file 1 and test some error conditions\n");

		if ((rc = pfm.OpenFile(FILE1, fh1))!= ReturnCode.PF_SUCCESS)
			return(rc);

		//  if ((rc = pfm.DestroyFile(FILE1)) != PF_FILEOPEN) {
		//    cout << "Destroy file while open should fail: ";
		//    return(rc);
		//  }

		if ((rc = fh1.disposePage(new PageNum(100))) != ReturnCode.PF_INVALIDPAGE) {
			System.out.println("Dispose invalid page should fail: ");
			return(rc);
		}

		// Get page 1

		if ((rc = fh1.getThisPage(new PageNum(1), ph))!= ReturnCode.PF_SUCCESS)
			return(rc);

		if ((rc = fh1.disposePage(new PageNum(1))) != ReturnCode.PF_PAGEPINNED) {
			System.out.println("Dispose pinned page should fail: ");
			return(rc);
		}

		if ((rc = ph.GetData(pData)) != ReturnCode.PF_SUCCESS||
				(rc = ph.GetPageNum(pageNum))!= ReturnCode.PF_SUCCESS)
			return(rc);

		//memcpy((char *)&temp, pData, sizeof(PageNum));
		pData.addr = Arrays.copyOf(temp.toString().getBytes(), temp.toString().length());

		if (temp.GetValue() != 1 || pageNum.GetValue() != 1) {
			System.out.println("Asked for page 1, got: "+pageNum+" "+temp+"\n");
			System.exit(0);
		}

		if ((rc = fh1.unpinPage(new PageNum(pageNum.GetValue())))!= ReturnCode.PF_SUCCESS)
			return(rc);

		if ((rc = fh1.unpinPage(new PageNum(pageNum.GetValue()))) != ReturnCode.PF_PAGEUNPINNED) {
			System.out.println("Unpin unpinned page should fail: ");
			return(rc);
		}

		System.out.println("Opening file 1 twice, printing out both copies\n");

		if ((rc = pfm.OpenFile(FILE1, fh2))!= ReturnCode.PF_SUCCESS)
			return(rc);

		if ((rc = PrintFile(fh1)) != ReturnCode.PF_SUCCESS||
				(rc = PrintFile(fh2))!= ReturnCode.PF_SUCCESS)
			return(rc);

		System.out.println("Closing and destroying both files\n");

		if ((rc = pfm.CloseFile(fh1)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.CloseFile(fh2)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.DestroyFile(FILE1)) != ReturnCode.PF_SUCCESS||
				(rc = pfm.DestroyFile(FILE2))!= ReturnCode.PF_SUCCESS)
			return(rc);

		// If we are dealing with statistics then we should output the final
		// numbers


		// Comment by Narcisse !
		//PF_Statistics();
		PF_ConfirmStatistics();


		// Return ok
		return ReturnCode.PF_SUCCESS;

	}

	 
	 //TODO ----------------------
	 /*
	ReturnCode TestHash(){

		PF_HashTable ht; //(Constant.PF_HASH_TBL_SIZE);
		ReturnCode           rc;
		int          i, s;
		PageNum      p;

		System.out.println( "Testing hash table.  Inserting entries\n");

		for (i = 1; i < 11; i++)
			for (p = 1; p < 11; p++)
				if ((rc = ht.Insert(i, p, i + p)))
					return(rc);

		Sytstem.out.println("Searching for entries\n");

		for (i = 1; i < 11; i++)
			for (p = 1; p < 11; p++)
				if ((rc = ht.Find(i, p, s)))
					return(rc);

		Sytstem.out.println("Deleting entries in reverse order\n");

		for (p = 10; p > 0; p--)
			for (i = 10; i > 0; i--)
				if ((rc = ht.Delete(i,p)))
					return(rc);

		Sytstem.out.println("Ensuring all entries were deleted\n");

		for (i = 1; i < 11; i++)
			for (p = 1; p < 11; p++)
				if ((rc = ht.Find(i, p, s)) != PF_HASHNOTFOUND) {
					Sytstem.out.println("Find deleted hash entry should fail: ");
					return(rc);
				}

		// Return ok
		return ReturnCode.PF_SUCCESS;;
	}
	
	*/

}

